
__author__ = ["Mark Gabby"]
__version__ = '0.0.2'
__bpydoc__ = """\
Avion Model Exporter Script.

"""

# TODO:
# Detect if the file already exists, and only show the "use existing" in this case
#  (Maybe default it to hidden and only show it if the file already exists and has valid metadata?)
# Detect if the specified license version is ported to the specified country
# Unify the license selection options, perhaps by expanding the FIFF library

from array import array
import fiff

import bpy
from bpy.props import (StringProperty,
                       BoolProperty,
                       EnumProperty,
                       )
import os
import getpass
import sys
import struct
import time
import calendar
# ExportHelper is a helper class, defines filename and
# invoke() function which calls the file selector.
from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty

class ExportAvionMdl(bpy.types.Operator, ExportHelper):
    '''Exports objects in the scene as .avionMdl file.'''
    bl_idname = 'export_scene.avion_avionmdl'  # this is important since its how bpy.ops.export.some_data is constructed
    bl_label = 'Export Avion Model'

    # ExportHelper mixin class uses this
    filename_ext = '.avionMdl'
    filter_glob = StringProperty(default='*.avionMdl', options={'HIDDEN'})

    use_existing_file_metadata = BoolProperty(
            name = "Use File Metadata",
            description = "Use metadata from the existing file, if available.",
            default=True )

    author = StringProperty( default=getpass.getuser() )
    
    copyright_license = EnumProperty(
            name="License",
            description = "(Note: I am not a lawyer and these descriptions are not legal advice.)",
            items = (('kAllRightsReserved', 'All Rights Reserved', "Default non-permissive license." ),
                    ('kPublicDomainReleased', 'Public Domain Released', "Released into the Public Domain by author." ),
                    ('kPublicDomain', 'Public Domain', "This work has lapsed copyright and is in the public domain." ),
                    ('kCC0', 'CC0', "Released into the Public Domain by author using Creative Commons." ),
                    ('kCC_BY', "CC BY", "Creative Commons: All uses permitted, but attribution required." ),
                    ('kCC_BY_SA', "CC BY-SA", "Creative Commons: All uses permitted, but attribution required and derivative works may only be distrbuted under an identical license." ),
                    ('kCC_BY_ND', "CC BY-ND", "Creative Commons: All uses permitted, but attribution required and work may not be altered, transformed, or built upon." ),
                    ('kCC_BY_NC', "CC BY-NC", "Creative Commons: All uses permitted, but attribution required and work may not be used commerically." ),
                    ('kCC_BY_NC_SA', "CC BY-NC-SA", "Creative Commons: All uses permitted, but attribution required, work may not be used commercially, and derivative works may only be distrbuted under an identical license." ),
                    ('kCC_BY_NC_ND', "CC BY-NC-ND", "Creative Commons: All uses permitted, but attribution required, work may not be used commercially, and work may not be altered, transformed, or built upon." )
                    ),
            default='kCC_BY_SA'
            )
    
    version = EnumProperty(
            name = "Version",
            description = "License version. I recommend using the most recent version available. This only applies to Creative Commons licenses. (Note: I am not a lawyer and this is not legal advice.)",
            items = (('kNA', "None", "No Version" ),
                     ('k1_0', "1.0", "Creative Commons Version 1.0 Licenses" ),
                     ('k2_0', "2.0", "Creative Commons Version 2.0 Licenses" ),
                     ('k2_5', "2.5", "Creative Commons Version 2.5 Licenses" ),
                     ('k3_0', "3.0", "Creative Commons Version 3.0 Licenses" ),
                     ('k4_0', "4.0", "Creative Commons Version 4.0 Licenses" )
                    ),
            default = "k3_0"
            )
    
    jurisdiction = EnumProperty(
            name = "Jurisdiction",
            description = "I recommend using No Country for Creative Commons licenses for unported versions. If you don't, make sure Creative Commons has a license in the selected jurisdiction.(Note: I am not a lawyer and this is not legal advice.)",
            items = (('kNoCountryCode', "No Country", ""),
##                     ("ABW", "Aruba", ""),
##                     ("AFG", "Afghanistan", ""),
##                     ("AGO", "Angola", ""),
##                     ("AIA", "Anguilla", ""),
##                     ("ALA", "Åland Islands", ""),
##                     ("ALB", "Albania", ""),
##                     ("AND", "Andorra", ""),
##                     ("ARE", "United Arab Emirates", ""),
                     ("ARG", "Argentina", ""),
##                     ("ARM", "Armenia", ""),
##                     ("ASM", "American Samoa", ""),
##                     ("ATA", "Antarctica", ""),
##                     ("ATF", "French Southern and Antarctic Lands|French Southern Territories", ""),
##                     ("ATG", "Antigua and Barbuda", ""),
                     ("AUS", "Australia", ""),
                     ("AUT", "Austria", ""),
##                     ("AZE", "Azerbaijan", ""),
##                     ("BDI", "Burundi", ""),
                     ("BEL", "Belgium", ""),
##                     ("BEN", "Benin", ""),
##                     ("BES", "Caribbean Netherlands|Bonaire, Sint Eustatius and Saba", ""),
##                     ("BFA", "Burkina Faso", ""),
##                     ("BGD", "Bangladesh", ""),
                     ("BGR", "Bulgaria", ""),
##                     ("BHR", "Bahrain", ""),
##                     ("BHS", "The Bahamas|Bahamas", ""),
##                     ("BIH", "Bosnia and Herzegovina", ""),
##                     ("BLM", "Saint Barthélemy", ""),
##                     ("BLR", "Belarus", ""),
##                     ("BLZ", "Belize", ""),
##                     ("BMU", "Bermuda", ""),
##                     ("BOL", "Bolivia|Bolivia, Plurinational State of", ""),
                     ("BRA", "Brazil", ""),
##                     ("BRB", "Barbados", ""),
##                     ("BRN", "Brunei|Brunei Darussalam", ""),
##                     ("BTN", "Bhutan", ""),
##                     ("BVT", "Bouvet Island", ""),
##                     ("BWA", "Botswana", ""),
##                     ("CAF", "Central African Republic", ""),
                     ("CAN", "Canada", ""),
##                     ("CCK", "Cocos (Keeling) Islands", ""),
                     ("CHE", "Switzerland", ""),
                     ("CHL", "Chile", ""),
                     ("CHN", "People's Republic of China|China", ""),
##                     ("CIV", "Côte d'Ivoire", ""),
##                     ("CMR", "Cameroon", ""),
##                     ("COD", "Democratic Republic of the Congo|Congo, the Democratic Republic of the", ""),
##                     ("COG", "Republic of the Congo|Congo", ""),
##                     ("COK", "Cook Islands", ""),
                     ("COL", "Colombia", ""),
##                     ("COM", "Comoros", ""),
##                     ("CPV", "Cape Verde", ""),
                     ("CRI", "Costa Rica", ""),
##                     ("CUB", "Cuba", ""),
##                     ("CUW", "Curaçao", ""),
##                     ("CXR", "Christmas Island", ""),
##                     ("CYM", "Cayman Islands", ""),
##                     ("CYP", "Cyprus", ""),
                     ("CZE", "Czech Republic", ""),
                     ("DEU", "Germany", ""),
##                     ("DJI", "Djibouti", ""),
##                     ("DMA", "Dominica", ""),
                     ("DNK", "Denmark", ""),
##                     ("DOM", "Dominican Republic", ""),
##                     ("DZA", "Algeria", ""),
                     ("ECU", "Ecuador", ""),
##                     ("EGY", "Egypt", ""),
##                     ("ERI", "Eritrea", ""),
##                     ("ESH", "Western Sahara", ""),
                     ("ESP", "Spain", ""),
                     ("EST", "Estonia", ""),
##                     ("ETH", "Ethiopia", ""),
                     ("FIN", "Finland", ""),
##                     ("FJI", "Fiji", ""),
##                     ("FLK", "Falkland Islands|Falkland Islands (Malvinas)", ""),
                     ("FRA", "France", ""),
##                     ("FRO", "Faroe Islands", ""),
##                     ("FSM", "Federated States of Micronesia|Micronesia, Federated States of", ""),
##                     ("GAB", "Gabon", ""),
                     ("GBR", "United Kingdom", ""),
##                     ("GEO", "Georgia (country)|Georgia", ""),
##                     ("GGY", "Guernsey", ""),
##                     ("GHA", "Ghana", ""),
##                     ("GIB", "Gibraltar", ""),
##                     ("GIN", "Guinea", ""),
##                     ("GLP", "Guadeloupe", ""),
##                     ("GMB", "The Gambia|Gambia", ""),
##                     ("GNB", "Guinea-Bissau", ""),
##                     ("GNQ", "Equatorial Guinea", ""),
                     ("GRC", "Greece", ""),
##                     ("GRD", "Grenada", ""),
##                     ("GRL", "Greenland", ""),
                     ("GTM", "Guatemala", ""),
##                     ("GUF", "French Guiana", ""),
##                     ("GUM", "Guam", ""),
##                     ("GUY", "Guyana", ""),
                     ("HKG", "Hong Kong", ""),
##                     ("HMD", "Heard Island and McDonald Islands", ""),
##                     ("HND", "Honduras", ""),
                     ("HRV", "Croatia", ""),
##                     ("HTI", "Haiti", ""),
                     ("HUN", "Hungary", ""),
##                     ("IDN", "Indonesia", ""),
##                     ("IMN", "Isle of Man", ""),
                     ("IND", "India", ""),
##                     ("IOT", "British Indian Ocean Territory", ""),
##                     ("IRL", "Republic of Ireland|Ireland", ""),
##                     ("IRN", "Iran|Iran, Islamic Republic of", ""),
##                     ("IRQ", "Iraq", ""),
##                     ("ISL", "Iceland", ""),
                     ("ISR", "Israel", ""),
                     ("ITA", "Italy", ""),
##                     ("JAM", "Jamaica", ""),
##                     ("JEY", "Jersey", ""),
##                     ("JOR", "Jordan", ""),
                     ("JPN", "Japan", ""),
##                     ("KAZ", "Kazakhstan", ""),
##                     ("KEN", "Kenya", ""),
##                     ("KGZ", "Kyrgyzstan", ""),
##                     ("KHM", "Cambodia", ""),
##                     ("KIR", "Kiribati", ""),
##                     ("KNA", "Saint Kitts and Nevis", ""),
                     ("KOR", "South Korea|Korea, Republic of", ""),
##                     ("KWT", "Kuwait", ""),
##                     ("LAO", "Laos|Lao People's Democratic Republic", ""),
##                     ("LBN", "Lebanon", ""),
##                     ("LBR", "Liberia", ""),
##                     ("LBY", "Libya|Libyan Arab Jamahiriya", ""),
##                     ("LCA", "Saint Lucia", ""),
##                     ("LIE", "Liechtenstein", ""),
##                     ("LKA", "Sri Lanka", ""),
##                     ("LSO", "Lesotho", ""),
##                     ("LTU", "Lithuania", ""),
                     ("LUX", "Luxembourg", ""),
##                     ("LVA", "Latvia", ""),
##                     ("MAC", "Macau|Macao", ""),
##                     ("MAF", "Collectivity of Saint Martin|Saint Martin (French part)", ""),
##                     ("MAR", "Morocco", ""),
##                     ("MCO", "Monaco", ""),
##                     ("MDA", "Moldova|Moldova, Republic of", ""),
##                     ("MDG", "Madagascar", ""),
##                     ("MDV", "Maldives", ""),
                     ("MEX", "Mexico", ""),
##                     ("MHL", "Marshall Islands", ""),
                     ("MKD", "Republic of Macedonia|Macedonia, the former Yugoslav Republic of", ""),
##                     ("MLI", "Mali", ""),
                     ("MLT", "Malta", ""),
##                     ("MMR", "Myanmar", ""),
##                     ("MNE", "Montenegro", ""),
##                     ("MNG", "Mongolia", ""),
##                     ("MNP", "Northern Mariana Islands", ""),
##                     ("MOZ", "Mozambique", ""),
##                     ("MRT", "Mauritania", ""),
##                     ("MSR", "Montserrat", ""),
##                     ("MTQ", "Martinique", ""),
##                     ("MUS", "Mauritius", ""),
##                     ("MWI", "Malawi", ""),
                     ("MYS", "Malaysia", ""),
##                     ("MYT", "Mayotte", ""),
##                     ("NAM", "Namibia", ""),
##                     ("NCL", "New Caledonia", ""),
##                     ("NER", "Niger", ""),
##                     ("NFK", "Norfolk Island", ""),
##                     ("NGA", "Nigeria", ""),
##                     ("NIC", "Nicaragua", ""),
##                     ("NIU", "Niue", ""),
                     ("NLD", "Netherlands", ""),
                     ("NOR", "Norway", ""),
##                     ("NPL", "Nepal", ""),
##                     ("NRU", "Nauru", ""),
                     ("NZL", "New Zealand", ""),
##                     ("OMN", "Oman", ""),
##                     ("PAK", "Pakistan", ""),
##                     ("PAN", "Panama", ""),
##                     ("PCN", "Pitcairn Islands|Pitcairn", ""),
                     ("PER", "Peru", ""),
                     ("PHL", "Philippines", ""),
##                     ("PLW", "Palau", ""),
##                     ("PNG", "Papua New Guinea", ""),
                     ("POL", "Poland", ""),
                     ("PRI", "Puerto Rico", ""),
##                     ("PRK", "North Korea|Korea, Democratic People's Republic of", ""),
                     ("PRT", "Portugal", ""),
##                     ("PRY", "Paraguay", ""),
##                     ("PSE", "Palestinian territories|Palestinian Territory, Occupied", ""),
##                     ("PYF", "French Polynesia", ""),
##                     ("QAT", "Qatar", ""),
##                     ("REU", "Réunion", ""),
                     ("ROU", "Romania", ""),
##                     ("RUS", "Russia|Russian Federation", ""),
##                     ("RWA", "Rwanda", ""),
##                     ("SAU", "Saudi Arabia", ""),
##                     ("SDN", "Sudan", ""),
##                     ("SEN", "Senegal", ""),
                     ("SGP", "Singapore", ""),
##                     ("SGS", "South Georgia and the South Sandwich Islands", ""),
##                     ("SHN", "Saint Helena, Ascension and Tristan da Cunha", ""),
##                     ("SJM", "Svalbard and Jan Mayen", ""),
##                     ("SLB", "Solomon Islands", ""),
##                     ("SLE", "Sierra Leone", ""),
##                     ("SLV", "El Salvador", ""),
##                     ("SMR", "San Marino", ""),
##                     ("SOM", "Somalia", ""),
##                     ("SPM", "Saint Pierre and Miquelon", ""),
                     ("SRB", "Serbia", ""),
##                     ("SSD", "South Sudan", ""),
##                     ("STP", "São Tomé and Príncipe|Sao Tome and Principe", ""),
##                     ("SUR", "Suriname", ""),
##                     ("SVK", "Slovakia", ""),
                     ("SVN", "Slovenia", ""),
                     ("SWE", "Sweden", ""),
##                     ("SWZ", "Swaziland", ""),
##                     ("SXM", "Sint Maarten|Sint Maarten (Dutch part)", ""),
##                     ("SYC", "Seychelles", ""),
##                     ("SYR", "Syria|Syrian Arab Republic", ""),
##                     ("TCA", "Turks and Caicos Islands", ""),
##                     ("TCD", "Chad", ""),
##                     ("TGO", "Togo", ""),
                     ("THA", "Thailand", ""),
##                     ("TJK", "Tajikistan", ""),
##                     ("TKL", "Tokelau", ""),
##                     ("TKM", "Turkmenistan", ""),
##                     ("TLS", "East Timor |T imor-Leste", ""),
##                     ("TON", "Tonga", ""),
##                     ("TTO", "Trinidad and Tobago", ""),
##                     ("TUN", "Tunisia", ""),
##                     ("TUR", "Turkey", ""),
##                     ("TUV", "Tuvalu", ""),
                     ("TWN", "Taiwan|Taiwan, Province of China", "(The UN considers Taiwan as part of China, so \"Taiwan, Province of China\" is the country name used in ISO 3166.)"),
##                     ("TZA", "Tanzania|Tanzania, United Republic of", ""),
##                     ("UGA", "Uganda", ""),
##                     ("UKR", "Ukraine", ""),
##                     ("UMI", "United States Minor Outlying Islands", ""),
##                     ("URY", "Uruguay", ""),
                     ("USA", "United States", ""),
##                     ("UZB", "Uzbekistan", ""),
##                     ("VAT", "Vatican City|Holy See (Vatican City State)", ""),
##                     ("VCT", "Saint Vincent and the Grenadines", ""),
##                     ("VEN", "Venezuela|Venezuela, Bolivarian Republic of", ""),
##                     ("VGB", "British Virgin Islands|Virgin Islands, British", ""),
##                     ("VIR", "United States Virgin Islands|Virgin Islands, U.S.", ""),
                     ("VNM", "Vietnam|Viet Nam", "(\"Viet Nam\" is the country name used in ISO 3166)"),
##                     ("VUT", "Vanuatu", ""),
##                     ("WLF", "Wallis and Futuna", ""),
##                     ("WSM", "Samoa", ""),
##                     ("YEM", "Yemen", ""),
                     ("ZAF", "South Africa", ""),
##                     ("ZMB", "Zambia", ""),
##                     ("ZWE", "Zimbabwe", ""),
                    ),
            default="kNoCountryCode"
        )
    def execute(self, context):
        return write_avionMdl( context, self.filepath, self.use_existing_file_metadata, self.author, self.copyright_license, self.version, self.jurisdiction )

def create_array(string):
    ret = array('b')
    for c in string:
        ret.append(ord(c))
    return ret

def get_face_vertices(face, mesh, matrix):
    fv = []
    for vertex in face.vertices:
        fv.append(mesh.vertices[vertex].co * matrix)
    return fv

def get_vertices(context):
    ret = array('f')
    for obj in context.scene.objects:
        if obj.type == 'MESH':
            matrix = obj.matrix_world

            mesh = obj.to_mesh(bpy.context.scene, True, "PREVIEW")

            for face in mesh.faces:
                face_vertices = get_face_vertices( face, mesh, matrix )
                for vertex in face_vertices:
                    ret.append(-vertex.x)
                    ret.append(vertex.z)
                    ret.append(vertex.y)
            bpy.data.meshes.remove(mesh)
    return ret

def get_face_normals(face, mesh, matrix):
    fn = []
    for vertex in face.vertices:
        fn.append(mesh.vertices[vertex].normal * matrix)
    return fn

def get_normals(context):
    ret = array('f')
    for obj in context.scene.objects:
        if obj.type == 'MESH':
            matrix = obj.matrix_world

            mesh = obj.to_mesh(bpy.context.scene, True, "PREVIEW")

            for face in mesh.faces:
                face_normals = get_face_normals( face, mesh, matrix )
                for normal in face_normals:
                    ret.append(-normal.x)
                    ret.append(normal.z)
                    ret.append(normal.y)
            bpy.data.meshes.remove(mesh)
    return ret

def get_texture_coords( context ):
    # Create an array of floats for the texture coordinates, which are
    # floating-point values, ideally from 0 to 1
    ret = array('f')
    for obj in context.scene.objects:
        if obj.type == 'MESH':
            matrix = obj.matrix_world

            mesh = obj.to_mesh(bpy.context.scene, True, "PREVIEW")

            layer = mesh.uv_textures[0];
            for face in layer.data:
                ret.face.uv1
                
            for face in mesh.uv_textures:
                texture_coordinates = get_face_texture_coordinates( face, mesh, matrix )
                for coordinate in texture_coordinates:
                    ret.append(coordinate.u)
                    ret.append(coordinate.v)
            bpy.data.meshes.remove(mesh)
    return ret

def get_face_indices( context ):
    ret = array('I')
    if ( ret.itemsize != 4 ):
        ret = array( 'L' )

    cur_index = 0
    for obj in context.scene.objects:
        if obj.type == 'MESH':
            matrix = obj.matrix_world

            mesh = obj.to_mesh(bpy.context.scene, True, "PREVIEW")
            for face in mesh.faces:
                # Single triangle - just each index in order
                if len(face.vertices) == 3:
                    ret.append( cur_index )
                    cur_index += 1
                    ret.append( cur_index )
                    cur_index += 1
                    ret.append( cur_index )
                    cur_index += 1
                # Square face - break into triangles
                elif len(face.vertices) == 4:
                    ret.append( cur_index + 0 )
                    ret.append( cur_index + 1 )
                    ret.append( cur_index + 2 )

                    ret.append( cur_index + 0 )
                    ret.append( cur_index + 2 )
                    ret.append( cur_index + 3 )
                    cur_index += 4
            bpy.data.meshes.remove(mesh)
    return ret

def write_avionMdl(context, filepath, use_existing_file_metadata, author, copyright_license, version, jurisdiction):
    # Create vertex, normal, color, specular color, spec power, texture coordinate,
    # and face index lists
    vertex_array = get_vertices( context )
    normal_array = get_normals( context )
    texture_coord_array = [] # get_texture_coords( context )
    face_index_array = get_face_indices( context )
    color_array = [] # get_colors( context )
    specular_color_array = [] # get_specular_colors( context )
    specular_power_array = [] # get_spec_powers( context )

    texture_map_name = ""
    normal_map_name = ""

    output_file_name = os.path.basename( filepath )
    output_file_extensionless = os.path.splitext( output_file_name )[0]

    # Try to get existing metadata if this was specified by the exporting user
    got_existing_metadata = False
    license_string = ""
    creation_time = None
    if ( use_existing_file_metadata ):
        if ( fiff.file_begin( filepath, "kExisting" ) ):
            if ( fiff.read_object_begin( ( "avionMdl", "aMdl" ) ) ):
                if ( fiff.get_chunk_metadata_author() != None ):
                    author = fiff.get_chunk_metadata_author()
                    license_string = fiff.get_chunk_metadata_license()
                    creation_time = fiff.get_chunk_metadata_creation_time()
                    got_existing_metadata = True
                fiff.read_object_end( ( "avionMdl", "aMdl" ) )
            fiff.file_end()

    fiff.file_begin( filepath, "kNewOrOverwrite" )
    fiff.write_object_begin( ( "avionMdl", "aMdl" ), output_file_extensionless )

    if ( got_existing_metadata ):
        fiff.write_chunk_begin( "METADATA" )

        fiff.write_chunk_begin( "AUTHOR" )
        fiff.write_string( author )
        fiff.write_chunk_end( "AUTHOR" )

        fiff.write_chunk_begin( "LICENSE" )
        fiff.write_string( license_string )
        fiff.write_chunk_end( "LICENSE" )

        fiff.write_chunk_begin( "CR TIME" )
        fiff.write_uint64( creation_time )
        fiff.write_chunk_end( "CR TIME" )

        fiff.write_chunk_begin( "MOD TIME" )
        fiff.write_uint64( fiff.get_fiff_epoch_timestamp() )
        fiff.write_chunk_end( "MOD TIME" )
        
        fiff.write_chunk_end( "METADATA" )
    else:
        fiff.write_metadata_chunk( author, copyright_license, version, jurisdiction )

    if ( len( texture_map_name ) > 0 ):
        fiff.write_chunk_begin( ( "Texture", "Txtr" ) )
        fiff.write_chunk_end( ( "Texture", "Txtr" ) )

    fiff.write_chunk_begin( ( "Vertices", "Vrts" ) )
    fiff.write_float32s( vertex_array )
    fiff.write_chunk_end( ( "Vertices", "Vrts" ) )

    fiff.write_chunk_begin( ( "Normals", "Nrms" ) )
    fiff.write_float32s( normal_array )
    fiff.write_chunk_end( ( "Normals", "Nrms" ) )

    fiff.write_chunk_begin( ( "Indices", "Idxs" ) )
    fiff.write_uint32s( face_index_array )
    fiff.write_chunk_end( ( "Indices", "Idxs" ) )

    fiff.write_object_end( ( "avionMdl", "aMdl" ) )

    fiff.file_end()

    return {'FINISHED'}
